package com.esunny.httpclient;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.management.ManagementFactory;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.zip.GZIPInputStream;
import java.util.zip.InflaterInputStream;

import javax.net.ssl.SSLHandshakeException;

import org.apache.commons.lang.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.ProtocolException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.RedirectStrategy;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.URIUtils;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.FileBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.client.DefaultRedirectStrategy;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 类HttpClinetServiceImpl.java的实现描述：httpclinet
 * 
 * @author zyy 2012-3-20 下午2:07:52
 */
public class HttpClientServiceImpl {

    /**
     * slf4j
     */
    private static final Logger logger            = LoggerFactory.getLogger(HttpClientServiceImpl.class);

    /**
     * user_agent
     */
    private static final String USER_AGENT        = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.79 Safari/535.11";

    /**
     * 最大连接数
     */
    private int                 maxConnections    = 5000;

    /**
     * 链接超时时间
     */
    private int                 connectionTimeout = 1000;

    /**
     * 每个主机最大连接数
     */
    private int                 maxPerRoute       = 100;

    /**
     * 读取超时时间
     */
    private int                 readTimeout       = 30000;

    /**
     * 请求失败后重试次数
     */
    private int                 retry             = 1;

    /**
     * httpClient
     */
    private HttpClient          httpClient;

    public void init() throws Exception {
        ThreadSafeClientConnManager manager = new ThreadSafeClientConnManager();
        manager.setMaxTotal(maxConnections);
        manager.setDefaultMaxPerRoute(maxPerRoute);
        HttpParams params = new BasicHttpParams();
        params.setParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, connectionTimeout);
        params.setParameter(CoreConnectionPNames.SO_TIMEOUT, readTimeout);
        DefaultHttpClient httpClient = new DefaultHttpClient(manager, params);

        httpClient.setRedirectStrategy(new RedirectStrategy() {

            @Override
            public boolean isRedirected(org.apache.http.HttpRequest request, HttpResponse response, HttpContext context) {
                return false;
            }

            @Override
            public HttpUriRequest getRedirect(org.apache.http.HttpRequest request, HttpResponse response,
                                              HttpContext context) throws ProtocolException {
                // TODO Auto-generated method stub
                return null;
            }
        });
        httpClient.setHttpRequestRetryHandler(new HttpRequestRetryHandler() {

            @Override
            public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
                if (executionCount <= retry && !(exception instanceof SSLHandshakeException)) {
                    logger.error(exception.getMessage(), exception);
                    logger.error("executionCount:{}", executionCount);
                    logger.error("retry request!");
                    return true;
                }
                return false;
            }
        });
        this.httpClient = httpClient;
    }

    /**
     * 通过HTTP请求调用,获得HTTP响应内容
     * 
     * @param requestUrl 请求URL
     * @param isPost 是否是post方式提交
     * @param params 参数
     * @return
     * @throws IOException
     * @throws URISyntaxException
     * @throws ClientProtocolException
     */
    public String getHttpResponseContent(String requestUrl, Map<String, Object> params, boolean isPost)
                                                                                                       throws URISyntaxException,
                                                                                                       ClientProtocolException,
                                                                                                       IOException {
        HttpRequest request = getRequest(requestUrl, params, isPost);
        return request.getResponseAsString();
    }

    /**
     * 通过HTTP请求调用,获得HTTP响应内容 -(Stream)调用处要关闭流
     * 
     * @param requestUrl 请求URL
     * @param params 参数
     * @param isPost 是否是post方式提交
     * @param allowCompression 是否允许压缩
     * @return
     * @throws IOException
     * @throws URISyntaxException
     * @throws ClientProtocolException
     */
    public InputStream getHttpResponseStream(String requestUrl, Map<String, Object> params, boolean isPost,
                                             boolean allowCompression) throws URISyntaxException,
                                                                      ClientProtocolException, IOException {
        HttpRequest request = getRequest(requestUrl, params, isPost);
        InputStream result = request.getResponseAsStream(allowCompression);
        // 在调用处要关闭流
        // request.release();
        return result;
    }

    /**
     * 得到request
     * 
     * @throws URISyntaxException
     * @throws UnsupportedEncodingException
     */
    private HttpRequest getRequest(String requestUrl, Map<String, Object> params, boolean isPost)
                                                                                                 throws UnsupportedEncodingException,
                                                                                                 URISyntaxException {
        if (StringUtils.isBlank(requestUrl)) {
            throw new IllegalArgumentException("requestUrl is null.");
        }
        HttpRequestBase httpMethod = null;
        if (isPost) {
            HttpPost post = new HttpPost(requestUrl);
            httpMethod = post;
            if (params != null && !params.isEmpty()) {
                MultipartEntity multipartEntity = new MultipartEntity();
                for (Entry<String, Object> entry : params.entrySet()) {
                    if (entry.getValue() instanceof String) {
                        StringBody stringBody = new StringBody(entry.getValue().toString(), Charset.forName("UTF-8"));
                        multipartEntity.addPart(entry.getKey(), stringBody);
                    } else if (entry.getValue() instanceof File) {
                        FileBody fileBody = new FileBody(File.class.cast(entry.getValue()));
                        multipartEntity.addPart(entry.getKey(), fileBody);
                    }
                }
                post.setEntity(multipartEntity);
            }
        } else {
            if (params != null && !params.isEmpty()) {
                List<NameValuePair> nameValuePairList = new ArrayList<NameValuePair>();
                for (Entry<String, Object> entry : params.entrySet()) {
                    if (entry.getValue() == null) {
                        continue;
                    }
                    nameValuePairList.add(new BasicNameValuePair(entry.getKey(), entry.getValue().toString()));
                }
                URI uri = new URI(requestUrl);
                uri = URIUtils.createURI(uri.getScheme(), uri.getHost(), uri.getPort(), uri.getPath(),
                                         URLEncodedUtils.format(nameValuePairList, "UTF-8"), null);
                httpMethod = new HttpGet(uri);
            } else {
                httpMethod = new HttpGet(requestUrl);
            }
        }
        if (logger.isInfoEnabled()) {
            logger.info("begin to get response through http.");
            logger.info("requestUrl:[{}]", requestUrl);
            logger.info("isPost:[{}]", isPost);
        }
        return new HttpRequest(httpClient, httpMethod);
    }

    /**
     * 类HttpClinetServiceImpl.java的实现描述：httpRequest
     * 
     * @author zyy 2012-3-20 下午4:50:05
     */
    class HttpRequest {

        /**
         * httpclient
         */
        private HttpClient      httpClient;

        /**
         * httpMethod
         */
        private HttpRequestBase httpMethod;

        public HttpRequest(HttpClient httpClient, HttpRequestBase httpMethod){
            super();
            this.httpClient = httpClient;
            this.httpMethod = httpMethod;
            this.httpMethod.addHeader("User-Agent", USER_AGENT);// 防止屏蔽
        }

        /**
         * 获得返回字符串
         * 
         * @return
         * @throws ClientProtocolException
         * @throws IOException
         */
        public String getResponseAsString() throws ClientProtocolException, IOException {
            HttpResponse response = httpClient.execute(httpMethod);
            int statusCode = response.getStatusLine().getStatusCode();
            HttpEntity entity = response.getEntity();
            InputStream in = entity.getContent();
            if (statusCode == HttpStatus.SC_OK) {

                return EntityUtils.toString(entity, "utf-8");
            } else {
                httpMethod.abort();
                logger.error("request data failed, statusCode={}", statusCode);
            }
            return null;
        }

        /**
         * 获得返回的流
         * 
         * @param allowCompression
         * @return
         * @throws ClientProtocolException
         * @throws IOException
         */
        public InputStream getResponseAsStream(boolean allowCompression) throws ClientProtocolException, IOException {
            httpMethod.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
            if (allowCompression) {
                httpMethod.setHeader("Accept-Encoding", "gzip,deflate");
            }
            HttpResponse response = httpClient.execute(httpMethod);
            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == HttpStatus.SC_OK) {
                HttpEntity entity = response.getEntity();
                InputStream input = entity.getContent();
                if (allowCompression && input != null) {
                    Header contentEncodingHeader = entity.getContentEncoding();
                    if (contentEncodingHeader != null) {
                        String contentEncoding = contentEncodingHeader.getValue();
                        if (contentEncoding.contains("gzip")) {
                            input = new GZIPInputStream(input);
                        } else if (contentEncoding.contains("deflate")) {
                            input = new InflaterInputStream(input);
                        }
                    }
                } else {
                    Header contentTypeHeader = entity.getContentType();
                    if (contentTypeHeader != null) {
                        String contentType = contentTypeHeader.getValue();
                        if (contentType != null) {
                            if (contentType.startsWith("application/x-gzip-compressed")) {
                                input = new GZIPInputStream(input);
                            } else if (contentType.startsWith("application/x-deflate")) {
                                input = new InflaterInputStream(input);
                            }
                        }
                    }
                }
                return input;
            } else {
                httpMethod.abort();
                logger.error("request data failed, statusCode=[{}]", statusCode);
            }
            return null;
        }
    }

    public int getMaxConnections() {
        return maxConnections;
    }

    public void setMaxConnections(int maxConnections) {
        this.maxConnections = maxConnections;
    }

    public int getConnectionTimeout() {
        return connectionTimeout;
    }

    public void setConnectionTimeout(int connectionTimeout) {
        this.connectionTimeout = connectionTimeout;
    }

    public int getMaxPerRoute() {
        return maxPerRoute;
    }

    public void setMaxPerRoute(int maxPerRoute) {
        this.maxPerRoute = maxPerRoute;
    }

    public int getReadTimeout() {
        return readTimeout;
    }

    public void setReadTimeout(int readTimeout) {
        this.readTimeout = readTimeout;
    }

    public int getRetry() {
        return retry;
    }

    public void setRetry(int retry) {
        this.retry = retry;
    }

    public static void main(String[] args) throws Exception {
        System.out.println(ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getMax());
        // HttpClientServiceImpl client = new HttpClientServiceImpl();
        // client.setConnectionTimeout(30000);
        // client.setMaxConnections(100);
        // client.setMaxPerRoute(50);
        // client.setReadTimeout(30000);
        // client.init();
        // // client.doPost("http://192.168.1.62:6003/publish?", "1212");
        // String sb = null;
        // for (int i = 0; i < 100; i++) {
        // sb = client.getHttpResponseContent("http://member.esunny.com/member/user_center.htm", null, false);
        // // InputStream in = client.getHttpResponseStream("http://im-dev.esunny.com:8013/do_im_login.htm", null,
        // // false,
        // // false);
        // System.out.println(i);
        // // client.doPost("http://192.168.1.62:6003/publish?", "1212");
        // }
        //
        // System.out.println(sb);
    }
}
